﻿using AutoMapper;
using ChineseAreaService;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json;
using Sino.CapacityCloud.Infrastructure.Events;
using Sino.CapacityCloud.Repositories.Models;
using Sino.CapacityCloud.Services;
using Sino.Tms.Primitives;
using Sino.WebApi.Framework.Core.Filters;
using Sino.WebApi.Framework.Responses;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Sino.CapacityCloud.WebApi.ApiControllers
{

    public class OrderController
        : ApiController<OrderController>
    {

        private readonly ITmsServiceBus _tmsService;
        private readonly IChinaAreaService _areaService;
        private readonly IEventBus _eventBus;

        internal static Dictionary<PriceUnit, string> PriceUnitMapping;

        static OrderController()
        {
            PriceUnitMapping = typeof(PriceUnit).GetTypeInfo().GetRuntimeFields()
                .Where(x => x.IsLiteral)
                .ToDictionary(z => (PriceUnit)z.GetValue(null), z => z.GetCustomAttribute<EnumTextMetaAttribute>().Text);
        }

        public OrderController(
            IHostingEnvironment env,
            IApplicationEnvironment applicationEnvironment,
            IDistributedCache cache,
            ITokenGenertationService tokenGenertationService,
            ApplicationDbContext dbContext,
            ApplicationUserManager userManager,
            ApplicationRoleManager roleManager,
            ApplicationSignInManager signInManager,
            IChinaAreaService areaService,
            IEventBus eventBus,
            ILoggerFactory loggerFactory,
            ITmsServiceBus tmsService)
            : base(env, applicationEnvironment, cache, tokenGenertationService, dbContext, userManager, roleManager, signInManager, loggerFactory)
        {
            this._tmsService = tmsService;
            this._areaService = areaService;
            this._eventBus = eventBus;
        }

        public async Task<JsonResult> GetOrderList(OrderSuppliedState state, string startingPlace, string destinationPlace, string carLength, int startIndex, int count)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    DateTime? currentDate = null;
                    if (this._hostingEnvironment.IsProduction())
                    {
                        currentDate = DateTime.Now.Date;
                    }
                    var orders = await this._tmsService.GetOrdersAsync(
                        startingPlace, destinationPlace,
                        currentDate, null, startIndex, count);
                    if (orders.List?.Count() > 0)
                    {
                        var lines = this.DataContext.FavouriteTransportLinkages
                            .AsNoTracking()
                            .Include(x => x.TransportLinkage)
                            .Where(x => x.UserId == user.Id).ToList();

                        List<Repositories.Models.Quotation> quotations;
                        //var realNameInfo = user.GetRealNameInfo();
                        //if (realNameInfo != null && realNameInfo.State == RealNameState.Pass)
                        //{
                        quotations = this.DataContext.Quotations
                            .AsNoTracking().Where(z => z.UserId == user.Id).ToList();
                        //}
                        //else
                        //{
                        //    quotations = new List<Repositories.Models.Quotation>();
                        //}

                        var list = orders.List.Select(x =>
                        {
                            var quotation = quotations.FirstOrDefault(z => z.OrderId == x.OrderId);
                            var r = Mapper.Map(x, new Order());
                            r.IsFocus = lines.Any(z => x.TransportLinkage.IsCompatiable(z.TransportLinkage));
                            r.OrderSuppliedState = quotation != null ? OrderSuppliedState.Grabbed : OrderSuppliedState.Ungrabbed;
                            r.GrabComment = quotation?.Remarks ?? "";
                            r.MyOrderState = MyOrderState.Waiting;
                            r.Price = SystemUtils.Try(() => $"{quotation.Price} {PriceUnitMapping[quotation.Unit]}", "");
                            return r;
                        }).ToArray();
                        var response = new OrderListResponse()
                        {
                            Orders = list,
                            Total = orders.Total
                        };
                        return await this.PackageResultAsync<OrderListResponse>(response);
                    }

                    return await this.PackageResultAsync<OrderListResponse>();
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get order infos because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<OrderListResponse>(ex);
            }
        }

        public async Task<JsonResult> GetMyInvolvedOrderList(int startIndex = 0, int count = -1)
        {
            try
            {
                if (startIndex < 0)
                {
                    throw new ArgumentOutOfRangeException($"{nameof(startIndex)}", "startIndex must greater than 0.");
                }
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    if (user.Car == null)
                    {
                        user.Car = this.DataContext.Cars
                            .AsNoTracking()
                            .FirstOrDefault(x => x.CarId == user.CarId);
                    }

                    var realNameInfo = user.GetRealNameInfo();
                    if (!(realNameInfo != null && realNameInfo.State == RealNameState.Pass))
                    {
                        return await this.PackageResultAsync<OrderListResponse>();
                    }

                    var now = DateTime.Now;
                    var list = new List<Order>();

                    var lines = this.DataContext.FavouriteTransportLinkages
                        .AsNoTracking()
                        .Include(x => x.TransportLinkage)
                        .Where(x => x.UserId == user.Id).ToList();

                    var quotations = this.DataContext.Quotations.AsNoTracking().Where(x => x.UserId == user.Id).ToList();

                    var grabbedOrderIds = quotations.Select(x => x.OrderId).Where(x => !string.IsNullOrEmpty(x)).ToList();
                    if (grabbedOrderIds != null && grabbedOrderIds.Count > 0)
                    {
                        var grabbedOrders = await this._tmsService.GetMyInvolvedOrdersAsync(grabbedOrderIds, skip: 0, count: -1);

                        if (grabbedOrders?.List?.Count() > 0)
                        {
                            foreach (var x in grabbedOrders.List)
                            {
                                var quotation = quotations.FirstOrDefault(z => z.OrderId == x.OrderId);
                                var r = Mapper.Map(x, new Order());
                                r.IsFocus = lines.Any(z => x.TransportLinkage.IsCompatiable(z.TransportLinkage));
                                r.OrderSuppliedState = OrderSuppliedState.Grabbed;
                                r.GrabComment = quotation?.Remarks ?? "";
                                r.MyOrderState = !string.IsNullOrWhiteSpace(x.CarrierOrderId) ?
                                    MyOrderState.Waiting : Mapper.Map<MyOrderState>(x.State);
                                r.Price = SystemUtils.Try(() => $"{quotation.Price} {PriceUnitMapping[quotation.Unit]}", "");

                                if (!string.IsNullOrEmpty(x.CarrierOrderId))
                                {
                                    if (string.Equals(x.CarCode, user.Car?.Code, StringComparison.InvariantCultureIgnoreCase))
                                    {
                                        r.OrderGrabbedState = OrderGrabbedState.Win;
                                        r.Id = x.CarrierOrderId;
                                    }
                                    else
                                    {
                                        r.OrderGrabbedState = OrderGrabbedState.Lose;
                                    }
                                }
                                else
                                {
                                    if (x.State == OrderStatus.TCHBD || x.State == OrderStatus.OEND)
                                    {
                                        r.OrderGrabbedState = OrderGrabbedState.Lose;
                                    }
                                    else
                                    {
                                        r.OrderGrabbedState = OrderGrabbedState.Pending;
                                    }
                                }

                                list.Add(r);
                            }
                        }
                    }

                    IEnumerable<Order> result = list.Skip(startIndex);

                    if (count > 0)
                    {
                        result = result.Take(count);
                    }

                    if (list.Count > 0)
                    {
                        var response = new OrderListResponse()
                        {
                            Orders = result.ToArray(),
                            Total = list.Count
                        };
                        return await this.PackageResultAsync<OrderListResponse>(response);
                    }

                    return await this.PackageResultAsync<OrderListResponse>();
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get order infos because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<OrderListResponse>(ex);
            }
        }

        public async Task<JsonResult> GetUnitInfo()
        {
            try
            {
                if (GenericCache<UnitInfoResponse>.Value == null)
                {
                    GenericCache<UnitInfoResponse>.Value = new UnitInfoResponse()
                    {
                        CarLength = typeof(CarLength).GetTypeInfo().GetRuntimeFields().Where(x => x.IsLiteral).Select(z => z.GetCustomAttribute<EnumTextMetaAttribute>().Text).Skip(1).ToArray(),
                        PriceUnit = typeof(PriceUnit).GetTypeInfo().GetRuntimeFields().Where(x => x.IsLiteral).Select(z => z.GetCustomAttribute<EnumTextMetaAttribute>().Text).Skip(1).ToArray()
                    };
                }
                return await this.PackageResultAsync<UnitInfoResponse>(GenericCache<UnitInfoResponse>.Value);
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get unit info items because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<VoidResponse>(ex);
            }
        }

        public async Task<JsonResult> GrabOrder(string orderId, double price, string priceUnit, string remarks)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    //if (user.IdentityCardCertificationState != CertificationStatus.Passed)
                    //{
                    //    throw new SystemException($"The current user {user.Id} is not certificated.");
                    //}
                    GrabOrderResponse response;
                    // An order can be grabbed by the user only once.
                    var quotation = this.DataContext.Quotations.AsNoTracking().FirstOrDefault(x => x.UserId == user.Id && x.OrderId == orderId);
                    if (quotation != null)
                    {
                        response = new GrabOrderResponse()
                        {
                            IsSuccesses = false,
                            Message = $"An order can be grabbed by the user only once."
                        };
                    }
                    else
                    {
                        try
                        {
                            var order = this.DataContext.Orders.AsNoTracking().FirstOrDefault(x => x.OrderId == orderId);
                            while (order == null)
                            {
                                try
                                {
                                    using (var body = this.HttpContext.Request.Body)
                                    {
                                        using (TextReader tr = new StreamReader(body))
                                        {
                                            var orderJson = tr.ReadToEnd();
                                            if (!string.IsNullOrWhiteSpace(orderJson))
                                            {
                                                var orderModel = JsonConvert.DeserializeObject<Order>(orderJson);
                                                if (!string.IsNullOrWhiteSpace(orderModel.Id))
                                                {
                                                    this.AddOrder(user.Id, orderModel);
                                                    break;
                                                }
                                            }
                                            throw new ArgumentException("Old protocol detected!");
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine +
                                        ex.InnerException?.Message + Environment.NewLine + ex.InnerException?.StackTrace);
                                    var r = await this._tmsService.GetOrderDetailsAsync(orderId);
                                    if (r?.Output != null)
                                    {
                                        order = Mapper.Map(r.Output, new Repositories.Models.Order());
                                        this.AddOrder(user.Id, order);
                                        break;
                                    }
                                    else
                                    {
                                        throw new ArgumentException($"The requested order {orderId} has been removed from the server.");
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine +
                                ex.InnerException?.Message + Environment.NewLine + ex.InnerException?.StackTrace);
                        }

                        var quotationModel = new Repositories.Models.Quotation()
                        {
                            OrderId = orderId,
                            Price = price,
                            Time = DateTime.Now,
                            Unit = priceUnit.ToEnumValue<PriceUnit, EnumTextMetaAttribute>(x => x.Text),
                            Remarks = remarks,
                            UserId = user.Id
                        };

                        this.DataContext.Quotations.Add(quotationModel);
                        this.DataContext.SaveChanges();

                        try
                        {
                            quotationModel = this.DataContext.Quotations
                                .Include(x => x.Order)
                                .ThenInclude(x => x.TransportLinkage)
                                .Include(x => x.User)
                                .ThenInclude(x => x.Car)
                                .FirstOrDefault(x => x.QuotationId == quotationModel.QuotationId);
                            if (quotationModel != null)
                            {
                                var eventData = new Sino.CommonServices.EventTypes.GrabbedOrderEventData();
                                Mapper.Map(quotationModel, eventData);
                                this._eventBus.Post(eventData, TimeSpan.Zero);
                            }
                        }
                        catch (Exception ex)
                        {
                            this._logger.LogWarning(ex.Message + Environment.NewLine + ex.StackTrace);
                        }

                        response = new GrabOrderResponse()
                        {
                            IsSuccesses = true,
                            Message = $"The user {user.Id} has grabbed the order {orderId}."
                        };
                    }
                    return await this.PackageResultAsync<GrabOrderResponse>(response);
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                var response = new GrabOrderResponse()
                {
                    IsSuccesses = false,
                    Message = $"Failed to grab the order because: {ex.Message + Environment.NewLine + ex.StackTrace}"
                };
                this._logger.LogError(response.Message);
                return await this.PackageResultAsync<GrabOrderResponse>(response);
            }
        }

        [SupressUserLogin()]
        public async Task<JsonResult> GetOrderQuotations(string orderId)
        {
            try
            {
                var quotations = this.DataContext.Quotations
                    .AsNoTracking()
                    .Where(x => string.IsNullOrEmpty(orderId) || string.Equals(x.OrderId, orderId, StringComparison.InvariantCultureIgnoreCase))
                    .Include(x => x.User)
                    .Include(x => x.Order)
                    .Include(x => x.User.Car)
                    .Select(x => new Quotation()
                    {
                        Id = x.QuotationId,
                        Description = x.Remarks,
                        OrderId = x.OrderId,
                        UserRealName = x.User.RealName,
                        PhoneNumber = x.User.PhoneNumber,
                        CarCode = x.User.Car.Code,
                        CarLength = x.User.Car.Length,
                        CarrierType = CarrierType.DR,
                        Price = x.Price,
                        Remarks = x.Remarks,
                        Time = x.Time,
                        Unit = x.Unit,
                        UserId = x.UserId,
                        CarType = x.User.Car.Type
                    }).ToArray();
                if (quotations.Length > 0)
                {
                    var response = new QuotationListResponse()
                    {
                        Quotations = quotations.ToArray()
                    };
                    return await this.PackageResultAsync<QuotationListResponse>(response);
                }
                return await this.PackageResultAsync<QuotationListResponse>();
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get quotation list because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<QuotationListResponse>(ex);
            }
        }

        public async Task<JsonResult> GetMyOrderList(int startIndex = 0, int count = -1)
        {
            try
            {
                if (startIndex < 0)
                {
                    throw new ArgumentOutOfRangeException($"{nameof(startIndex)}", "startIndex must greater than 0.");
                }
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    //var orders = this.DataContext.ConfirmedOrders
                    //    .AsNoTracking()
                    //    .Include(x => x.TransportLinkage)
                    //    //.Include(x => x.User)
                    //    .Include(x => x.Parent)
                    //    //.Include(x => x.SubOrders)
                    //    .Where(x => x.Parent == null && x.UserId == user.Id)
                    //    .OrderBy(x => x.State).ToList();
                    if (user.Car == null)
                    {
                        user.Car = this.DataContext.Cars
                            .AsNoTracking()
                            .FirstOrDefault(x => x.CarId == user.CarId);
                        if (user.Car == null)
                        {
                            return await this.PackageResultAsync<OrderListResponse>();
                        }
                    }

                    var realNameInfo = user.GetRealNameInfo();
                    if (!(realNameInfo != null && realNameInfo.State == RealNameState.Pass))
                    {
                        return await this.PackageResultAsync<OrderListResponse>();
                    }

                    var orders = await this._tmsService.GetMyOrdersAsync(user.Id, user.Car.Code, startIndex, count);
                    if (orders?.List?.Count() > 0)
                    {
                        var lines = this.DataContext.FavouriteTransportLinkages
                            .Include(x => x.TransportLinkage)
                            .Where(x => x.UserId == user.Id).ToList();

                        var quotations = this.DataContext.Quotations.AsNoTracking().Where(z => z.UserId == user.Id).ToList();

                        var response = new OrderListResponse()
                        {
                            Orders = orders.List.Select(x =>
                            {
                                var quotation = quotations.FirstOrDefault(z => z.OrderId == x.ConfirmedOrderId);
                                var r = Mapper.Map(x, new Order());
                                r.IsFocus = lines.Any(z => x.TransportLinkage.IsCompatiable(z.TransportLinkage));
                                r.GrabComment = quotation?.Remarks ?? "";
                                r.Price = SystemUtils.Try(() => $"{quotation.Price} {PriceUnitMapping[quotation.Unit]}", "");
                                r.OrderGrabbedState = OrderGrabbedState.Win;
                                r.OrderSuppliedState = OrderSuppliedState.Completed;
                                return r;
                            }).ToArray(),
                            Total = orders.Total
                        };
                        return await this.PackageResultAsync<OrderListResponse>(response);
                    }
                    return await this.PackageResultAsync<OrderListResponse>();
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get your orders because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<OrderListResponse>(ex);
            }
        }

        public async Task<JsonResult> GetSuborderList(string orderId)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    var orders = await this._tmsService.GetCarrierSubOrdersAsync(orderId);
                    if (orders?.Count() > 0)
                    {
                        var quotations = this.DataContext.Quotations.AsNoTracking().Where(z => z.UserId == user.Id).ToList();
                        var response = new OrderListResponse()
                        {
                            Orders = orders.Select(x =>
                            {
                                var quotation = quotations.FirstOrDefault(z => z.OrderId == x.ConfirmedOrderId);
                                var r = Mapper.Map(x, new Order());
                                r.GrabComment = quotation?.Remarks ?? "";
                                r.Price = SystemUtils.Try(() => $"{quotation.Price} {PriceUnitMapping[quotation.Unit]}", "");
                                r.OrderGrabbedState = OrderGrabbedState.Win;
                                r.OrderSuppliedState = OrderSuppliedState.Grabbed;
                                r.MyOrderState = !string.IsNullOrWhiteSpace(x.CarrierOrderId) ?
                                    MyOrderState.Waiting : Mapper.Map<MyOrderState>(x.State);
                                return r;
                            }).ToArray(),
                            Total = orders.Count()
                        };
                        return await this.PackageResultAsync<OrderListResponse>(response);
                    }
                    else
                    {
                        var order = await this._tmsService.GetOrderDetailsAsync(orderId);
                        if (!string.IsNullOrWhiteSpace(order?.Output?.CarrierOrderId))
                        {
                            return await this.GetSuborderList(order.Output.CarrierOrderId);
                        }
                    }
                    throw new SystemException($"Failed to find the specified Order object with id = {orderId}.");
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get sub orders because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<OrderListResponse>(ex);
            }
        }

        public async Task<JsonResult> GetCostList(string orderId)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    //var costs = this.DataContext.PayableCosts
                    //    .AsNoTracking()
                    //    .Where(x => x.OrderId == orderId).ToList();
                    if (user.Car == null)
                    {
                        user.Car = this.DataContext.Cars.FirstOrDefault(x => x.CarId == user.CarId);
                        if (user.Car == null)
                        {
                            return await this.PackageResultAsync<OrderListResponse>();
                        }
                    }
                    var histories = this.DataContext.PaymentHistories.AsNoTracking().Where(x => x.UserId == user.Id).OrderByDescending(x => x.LastPaymentTime).ToList();
                    var costs = await this._tmsService.QueryFeesAsync(orderId, user.Car.Code);
                    if (costs?.Output?.List?.Count() > 0)
                    {
                        var response = new CostListResponse()
                        {
                            Costs = costs.Output.List.Select(x =>
                            {
                                var r = Mapper.Map(x, new Cost());
                                r.LastPaymentTime = histories.FirstOrDefault(z =>
                                    z.SettleBillId == x.FeeSetttleBillId)?.LastPaymentTime;
                                return r;
                            }).ToArray()
                        };
                        return await this.PackageResultAsync<CostListResponse>(response);
                    }
                    return await this.PackageResultAsync<CostListResponse>();
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get your rating info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<CostListResponse>(ex);
            }
        }

        /// <summary>
        /// Get ratings for the specific carrier order.
        /// </summary>
        /// <param name="orderId">
        /// Carrier order id.
        /// </param>
        /// <returns></returns>
        public async Task<JsonResult> GetRatingList(string orderId)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    //var ratings = this.DataContext.Ratings
                    //    .AsNoTracking()
                    //    .Include(x => x.User)
                    //    .Where(x => x.UserId == user.Id && x.OrderId == orderId).ToList();
                    if (user.Car == null)
                    {
                        user.Car = this.DataContext.Cars.FirstOrDefault(x => x.CarId == user.CarId);
                        if (user.Car == null)
                        {
                            return await this.PackageResultAsync<OrderListResponse>();
                        }
                    }
                    var ratings = await this._tmsService.QueryEvaluationsAsync(orderId, user.Id);
                    if (ratings?.Output?.List?.Count() > 0)
                    {
                        var response = new RatingListResponse()
                        {
                            Ratings = ratings.Output.List.Select(x => new OrderRating()
                            {
                                Name = user.RealName,
                                Attitude = x.Attitude,
                                TransPrice = x.Freight,
                                Integrity = x.Credibility,
                                Timeliness = x.Timeliness,
                                Date = SystemUtils.Try(() => DateTime.Parse(x.CreationTime)),
                                Comment = x.Content,
                                TotalPoint = x.Average
                            }).ToArray()
                        };
                        return await this.PackageResultAsync<RatingListResponse>(response);
                    }
                    return await this.PackageResultAsync<RatingListResponse>();
                }
                throw new SystemException($"Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get your rating info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<RatingListResponse>(ex);
            }
        }

        /// <summary>
        /// 向指定收货人发送短信验证码
        /// </summary>
        /// <param name="carrierOrderId"></param>
        /// <param name="phoneNumber"></param>
        /// <returns></returns>
        public async Task<JsonResult> GetSmsCode(string carrierOrderId, string phoneNumber)
        {
            try
            {
                var result = await this._tmsService.GetSmsCodeAsync(carrierOrderId, phoneNumber);
                if (result != null && result.Output != null)
                {
                    return await this.PackageResultAsync<VoidResponse>();
                }
                throw new InvalidOperationException("Failed to send sms to the phone.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to send sms to the phone because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<VoidResponse>(ex);
            }
        }

        /// <summary>
        /// 确认收货
        /// </summary>
        /// <param name="carrierOrderId"></param>
        /// <param name="smsCode"></param>
        /// <returns></returns>
        public async Task<JsonResult> ConfirmDelivered(string carrierOrderId, string smsCode)
        {
            try
            {
                var result = await this._tmsService.ConfirmDeliveredAsync(carrierOrderId, smsCode);
                if (result != null && result.Output != null)
                {
                    if (!string.IsNullOrWhiteSpace(result.Output.ExtData))
                    {
                        return await this.PackageResultAsync<VoidResponse>(new Exception(result.Output.ExtData));
                    }
                    return await this.PackageResultAsync<VoidResponse>();
                }
                throw new InvalidOperationException($"Failed to confirm the sms code: {nameof(carrierOrderId)} = {carrierOrderId}, {nameof(smsCode)} = {smsCode}.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to confirm the sms code because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<VoidResponse>(ex);
            }
        }

        /// <summary>
        /// 获取收货人列表
        /// </summary>
        /// <param name="carrierOrderId"></param>
        /// <returns></returns>
        public async Task<JsonResult> GetConsigneeList(string carrierOrderId)
        {
            try
            {
                var result = await this._tmsService.GetConsigneeListAsync(carrierOrderId);
                if (result?.Output?.List?.Count() > 0)
                {
                    var response = new ConsigneeListResponse()
                    {
                        Consignees = result.Output.List.Select(x => Mapper.Map(x, new Consignee())).ToArray(),
                        Total = result.Output.Total
                    };
                    return await this.PackageResultAsync<ConsigneeListResponse>(response);
                }
                return await this.PackageResultAsync<ConsigneeListResponse>();
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get consignee list for carrier order '{carrierOrderId}' because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<ConsigneeListResponse>(ex);
            }
        }

        /// <summary>
        /// 获取附件列表
        /// </summary>
        /// <param name="carrierOrderId"></param>
        /// <returns></returns>
        public async Task<JsonResult> GetAttachmentList(string carrierOrderId)
        {
            try
            {
                var result = await this._tmsService.GetAttachmentListAsync(carrierOrderId);
                if (result?.Output?.List?.Count() > 0)
                {
                    var response = new ChildLineListResponse()
                    {
                        ChildLineItems = result.Output.List.Select(x => Mapper.Map(x, new ChildLineItem())).ToArray(),
                        Total = result.Output.Total
                    };
                    return await this.PackageResultAsync<ChildLineListResponse>(response);
                }
                return await this.PackageResultAsync<ChildLineListResponse>();
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get attachment list for carrier order '{carrierOrderId}' because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<ChildLineListResponse>(ex);
            }
        }

        /// <summary>
        /// 上传回单
        /// </summary>
        /// <param name="subLineId"></param>
        /// <param name="fileName"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<JsonResult> UploadReceipt(string subLineId, string fileName)
        {
            try
            {
                using (var body = this.HttpContext.Request.Body)
                {
                    using (var ms = new MemoryStream())
                    {
                        body.CopyTo(ms);
                        ms.Flush();
                        ms.Seek(0, SeekOrigin.Begin);
                        var length = ms.Length;
                        UploadResponse response;
                        //var fileName = $"receipt-{subLineId}-{Guid.NewGuid().ToString()}.png";
                        var result = await this._tmsService.UploadReceiptAsync(subLineId, fileName, ms);
                        if (result?.Output != null)
                        {
                            response = new UploadResponse()
                            {
                                IsSuccessed = true,
                                Uri = result.Output.FilePath,
                                Size = length,
                                Name = fileName
                            };
                        }
                        else
                        {
                            response = new UploadResponse()
                            {
                                IsSuccessed = false,
                                Uri = "",
                                Size = 0,
                                Name = ""
                            };
                        }
                        return await this.PackageResultAsync<UploadResponse>(response);
                        throw new System.Net.Http.HttpRequestException($"Failed to upload receipt for sub line '{subLineId}'.");
                    }
                }
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to upload receipt for sub line '{subLineId}' because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<ChildLineListResponse>(ex);
            }
        }

        /// <summary>
        /// 上传回单URL
        /// </summary>
        /// <param name="subLineId"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task<JsonResult> AddReceipt(string subLineId, string fileName, string url)
        {
            try
            {
                using (var body = this.HttpContext.Request.Body)
                {
                    //var fileName = $"receipt-{subLineId}-{Guid.NewGuid().ToString()}.png";
                    var result = await this._tmsService.AddReceiptAsync(subLineId, fileName, url);
                    if (result?.Output != null)
                    {
                        var response = new UploadResponse()
                        {
                            IsSuccessed = true,
                            Uri = result.Output.FilePath,
                            Size = body.Length,
                            Name = fileName
                        };
                        return await this.PackageResultAsync<UploadResponse>(response);
                    }
                    throw new System.Net.Http.HttpRequestException($"Failed to modify receipt for sub line '{subLineId}'.");
                }
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to upload modify for sub line '{subLineId}' because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<ChildLineListResponse>(ex);
            }
        }

        private Repositories.Models.Order AddOrder(string userId, Repositories.Models.Order order)
        {
            var line = this.AddOrUpdateTransportLinkage(order.TransportLinkage);
            if (line == null)
            {
                throw new InvalidDataException("Failed to get or add a line for the given order.");
            }
            order.TransportLinkage = line;
            order.TransportLinkageId = line.TransportLinkageId;
            this.DataContext.Orders.Add(order);
            this.DataContext.SaveChanges();
            return order;
        }

        private Repositories.Models.Order AddOrder(string userId, Order orderModel)
        {
            TransportLinkage line = null;
            if (orderModel.Line != null &&
                !string.IsNullOrWhiteSpace(orderModel.Line.StartingPlaceCode) &&
                !string.IsNullOrWhiteSpace(orderModel.Line.DestinationCode))
            {
                line = this.AddOrUpdateTransportLinkage(new TransportLinkage()
                {
                    StartingPlaceCode = orderModel.Line.StartingPlaceCode,
                    StartingPlaceName = this._areaService.GetAreaName(orderModel.Line.StartingPlaceCode),
                    DestinationPlaceCode = orderModel.Line.DestinationCode,
                    DestinationPlaceName = this._areaService.GetAreaName(orderModel.Line.DestinationCode),
                });
            }
            if (line == null)
            {
                throw new InvalidDataException("Failed to get or add a line for the given order.");
            }
            var order = new Repositories.Models.Order()
            {
                OrderId = orderModel.Id,
                OrderNumber = orderModel.OrderNumber,
                UserId = userId,
                TransportLinkageId = line.TransportLinkageId,
                Remarks = orderModel.Comment,
                Type = orderModel.Type,
                State = OrderStatus.ITC,
                Amount = orderModel.Amount,
                AmountUnit = orderModel.AmountUnit,
                Category = orderModel.Category,
                ArrivalTime = orderModel.ArrivalTime,
                DeliveryTime = orderModel.DeliveryTime,
                CarLength = orderModel.CarLength,
                ContactChatId = orderModel.ContactsChatId,
                ContactName = orderModel.Contacts,
                ContactPhoneNumber = orderModel.Telephone
            };
            this.DataContext.Orders.Add(order);
            this.DataContext.SaveChanges();
            return order;
        }

        private TransportLinkage AddOrUpdateTransportLinkage(TransportLinkage line)
        {
            var findEntity = this.DataContext.TransportLinkages.FirstOrDefault(x => x.Equals(line));
            if (findEntity == null)
            {
                this.DataContext.TransportLinkages.Add(line);
                this.DataContext.SaveChanges();
                return line;
            }
            return findEntity;
        }

    }

}